package main

import (
	"bufio"
	"bytes"
	"cliDemo/utils"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os/exec"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/mholt/archiver/v3"
	"github.com/wailsapp/wails/v2/pkg/runtime"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
)

// App struct
type App struct {
	ctx context.Context
}

type Task struct {
	Name   string `json:"name"`
	Dir    string `json:"dir"`
	Cmd    string `json:"cmd"`
	Params string `json:"params"`
	Build  string `json:"build"`
	Id     int    `json:"id"`
	Port   int    `json:"port"`
	Zip    bool   `json:"zip"`
	Remark string `json:"remark"`
}

type MsgInfo struct {
	Type string `json:"type"`
	Msg   string `json:"msg"`
}

// 实例
var TaskCmd = make(map[int]TaskCmdInfo)

type TaskCmdInfo struct {
	Cmd         *exec.Cmd
	Out         *bytes.Buffer
	ErrOut      *bytes.Buffer
	BuildOut    *bytes.Buffer
	BuildErrOut *bytes.Buffer
}

type charset string

const (
	UTF8    = charset("UTF-8")
	GB18030 = charset("GB18030")
)

// 日志类型
type TLogInfo struct {
	Log     string `json:"log"`
	LogType string `json:"logType"`
	Id      int    `json:"id"`
}

// 更新任务数据 类型
type TTaskUpdate struct {
	Id      int    `json:"id"`
	Port    string    `json:"port"`
}

// NewApp creates a new App application struct
func NewApp() *App {
	return &App{}
}

// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
	a.ctx = ctx
}

// Greet returns a greeting for the given name
func (a *App) Greet(name string) string {
	// runtime.FileFilter
	Filters := []runtime.FileFilter{}

	dig := runtime.OpenDialogOptions{
		DefaultDirectory:           "",
		DefaultFilename:            "",
		Title:                      name,
		Filters:                    Filters,
		ShowHiddenFiles:            false,
		CanCreateDirectories:       false,
		ResolvesAliases:            false,
		TreatPackagesAsDirectories: false,
	}

	selection, err := runtime.OpenDirectoryDialog(a.ctx, dig)
	if err != nil {
		fmt.Println(err)
	}
	return selection
	// return fmt.Sprintf("Hello %s, It's show time!", name)
}

// 返回目录
func (a *App) Dir() string {
	// runtime.FileFilter
	Filters := []runtime.FileFilter{}

	dig := runtime.OpenDialogOptions{
		DefaultDirectory:           "",
		DefaultFilename:            "",
		Title:                      "选择目录",
		Filters:                    Filters,
		ShowHiddenFiles:            false,
		CanCreateDirectories:       false,
		ResolvesAliases:            false,
		TreatPackagesAsDirectories: false,
	}

	selection, err := runtime.OpenDirectoryDialog(a.ctx, dig)
	if err != nil {
		fmt.Println(err)
	}
	return selection
	// return fmt.Sprintf("Hello %s, It's show time!", name)
}

// 获取信息
func (a *App) GetLog(taskStr string) string {
	fmt.Println(taskStr)
	// 将字符串转换为Task结构体
	var task Task
	err := json.Unmarshal([]byte(taskStr), &task)
	if err != nil {
		return "Error unmarshalling task:" + err.Error()
	}

	taskInfo, exists := TaskCmd[task.Id]
	if !exists || taskInfo.Cmd == nil {
		return "任务不存在"
	}

	fmt.Println(taskInfo.Out)

	type LogStrInfo struct {
		Start string `json:"start"`
		Err   string `json:"err"`
		Build string `json:"build"`
	}
	logInfo := &LogStrInfo{
		Start: StripANSI(taskInfo.Out.String()),
		Err:   StripANSI(taskInfo.ErrOut.String()),
		Build: StripANSI(taskInfo.BuildOut.String()),
	}

	str, _ := json.Marshal(logInfo)
	return string(str)
}

// 启动
func (a *App) Start(taskStr string) string {
	fmt.Println(taskStr)
	// 将字符串转换为Task结构体
	var task Task
	err := json.Unmarshal([]byte(taskStr), &task)
	if err != nil {
		return "Error unmarshalling task:" + err.Error()
	}
	params := strings.Split(task.Params, " ")
	// ctx, cancel := context.WithCancel(context.Background())
	// TaskCancle[task.Id] = cancel
	// cmd := exec.CommandContext(ctx, task.Cmd, params...)
	cmd := exec.Command(task.Cmd, params...)
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	cmd.Dir = task.Dir
	TaskCmd[task.Id] = TaskCmdInfo{
		Cmd:    cmd,
		Out:    &bytes.Buffer{},
		ErrOut: &bytes.Buffer{},
	}
	// cmd.Stdout = TaskCmd[task.Id].Out
	// cmd.Stderr = TaskCmd[task.Id].ErrOut

	fmt.Println("task", task)

	// 获取命令的标准输出和标准错误
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("Error getting stdout pipe:", err)
		return "错误获取标准输出"
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		fmt.Println("Error getting stderr pipe:", err)
		return "错误获取标准错误"
	}

	// 启动命令
	if err := cmd.Start(); err != nil {
		fmt.Println("Error starting command:", err)
		return "启动失败"
	}

	// 创建两个goroutine来持续监听标准输出和标准错误
	go func() {
		scanner := bufio.NewScanner(stdout)
		for scanner.Scan() {
			// fmt.Println("stdout:", scanner.Text())
			logStr := StripANSI(scanner.Text())

			regex := regexp.MustCompile(`http://(127.0.0.1|localhost):(\d+)/`)
			matches := regex.FindStringSubmatch(logStr)
			if len(matches) > 2 {
				resStr := fmt.Sprintf("%s",matches[2]);
				fmt.Println("Extracted Port:", resStr)
				taskUpdate := TTaskUpdate{
					Id: task.Id,
					Port: matches[2],
				}
				v, _ := json.Marshal(taskUpdate)
				runtime.EventsEmit(a.ctx, "update", string(v))
				fmt.Println("发送端口事件", string(v))
			} else {
				fmt.Println("No match found")
			}

			fmt.Println("触发监听器 运行日志：", logStr)
			logInfo := &TLogInfo{
				Log:     logStr,
				Id:      task.Id,
				LogType: "startIdLog",
			}
			v, _ := json.Marshal(logInfo)
			runtime.EventsEmit(a.ctx, "log", string(v))
		}
	}()

	go func() {
		scanner := bufio.NewScanner(stderr)
		for scanner.Scan() {
			// fmt.Println("stderr:", scanner.Text())
			logStr := StripANSI(scanner.Text())
			fmt.Println("触发监听器 错误日志：", logStr)
			logInfo := &TLogInfo{
				Log:     logStr,
				Id:      task.Id,
				LogType: "errIdLog",
			}
			v, _ := json.Marshal(logInfo)
			runtime.EventsEmit(a.ctx, "log", string(v))
		}
	}()

	// 等待命令完成
	// if err := cmd.Wait(); err != nil {
	// 	fmt.Println("Command finished with error:", err)
	// } else {
	// 	fmt.Println("Command finished successfully")
	// }
	fmt.Println("实例id:", cmd.Process.Pid)
	fmt.Println("...等待...")
	limit := 1
	lastLog := ""
	resStr := ""
	for {
		time.Sleep(time.Second * 1)
		limit += 1
		if TaskCmd[task.Id].Out.String() != lastLog {
			lastLog = TaskCmd[task.Id].Out.String()
			fmt.Println("信息改变:", time.Now().Format("2006-01-02 15:04:05"))
		}
		if limit > 2 && lastLog == TaskCmd[task.Id].Out.String() {
			fmt.Println("大于2秒 且 信息没有变动")
			break
		}
	}
	return resStr
}

// 停止
func (a *App) Stop(taskStr string) string {
	fmt.Println(taskStr)
	// 将字符串转换为Task结构体
	var task Task
	err := json.Unmarshal([]byte(taskStr), &task)
	if err != nil {
		return "Error unmarshalling task:" + err.Error()
	}

	if task.Port > 0 {
		// 找 端口 pid
		pid := GetFindPortPid(fmt.Sprintf("%d", task.Port))
		fmt.Println(pid)
		if pid == "" || pid == "0" {
			return "未找到进程"
		}

		// 结束pid
		KillPid(pid)
		// taskkill /f /pid 进程id
	} else {
		taskCmdInfo := TaskCmd[task.Id]
		if taskCmdInfo.Cmd == nil {
			return "未找到进程cmd"
		}
		taskCmdInfo.Cmd.Process.Kill()
	}

	return "ok"
}

// 打包
func (a *App) Build(taskStr string) string {
	fmt.Println(taskStr)
	// 将字符串转换为Task结构体
	var task Task
	err := json.Unmarshal([]byte(taskStr), &task)
	if err != nil {
		return "Error unmarshalling task:" + err.Error()
	}

	taskInfo, exists := TaskCmd[task.Id]
	if !exists || taskInfo.Cmd == nil {
		taskInfo.BuildOut = &bytes.Buffer{}
		taskInfo.BuildErrOut = &bytes.Buffer{}
	}

	// 定义要执行的命令
	fmt.Print("build task.Cmd", task.Cmd, task.Build)
	buildParams := strings.Split(task.Build, " ")
	cmd := exec.Command(task.Cmd, buildParams...)
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	cmd.Dir = task.Dir
	fmt.Println("task", task)

	// 获取命令的标准输出和标准错误
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("Error getting stdout pipe:", err)
		return "错误获取标准输出"
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		fmt.Println("Error getting stderr pipe:", err)
		return "错误获取标准错误"
	}

	// 启动命令
	if err := cmd.Start(); err != nil {
		fmt.Println("Error starting command:", err)
		return "启动失败"
	}

	// 创建两个goroutine来持续监听标准输出和标准错误
	go func() {
		scanner := bufio.NewScanner(stdout)
		for scanner.Scan() {
			// fmt.Println("stdout:", scanner.Text())
			logStr := StripANSI(scanner.Text())
			fmt.Println("触发监听器 打包日志：", logStr)
			logInfo := &TLogInfo{
				Log:     logStr,
				Id:      task.Id,
				LogType: "buildIdLog",
			}
			v, _ := json.Marshal(logInfo)
			runtime.EventsEmit(a.ctx, "log", string(v))
			logStrIndex := strings.Index(logStr, "built in")
			if logStrIndex != -1 {
				// 打包完成提示
				time.Sleep(time.Second * 1)
				logInfo.Log = "打包完成,目录：" + task.Dir + "/dist"
				v, _ := json.Marshal(logInfo)
				runtime.EventsEmit(a.ctx, "log", string(v))

				// 打包完成提示
				if task.Zip {
					time.Sleep(time.Second * 5)
					fmt.Println("进行压缩文件")
					indexFlag := utils.FileExists(task.Dir + "/dist/index.html")
					if !indexFlag {
						fmt.Println("压缩文件失败 没有 index.html 文件")
						return
					}
					zipFileName := task.Dir + "/dist/dist.zip"
					err := archiver.Archive([]string{task.Dir + "/dist"}, zipFileName)
					if err != nil {
						fmt.Println("压缩文件失败", err)
					}

					// utils.CompressDirectory(task.Dir+"/dist", zipFileName)
					// utils.ZipFolder(task.Dir+"/dist", zipFileName)
					logInfo.Log = "压缩成功,文件" + zipFileName
					fmt.Println("压缩成功,文件" + zipFileName)
					v, _ := json.Marshal(logInfo)
					runtime.EventsEmit(a.ctx, "log", string(v))

					msgInfo := MsgInfo{
						Type: "success",
						Msg:  "打包成功",
					}
					v, _ = json.Marshal(msgInfo)
					runtime.EventsEmit(a.ctx, "msg", string(v))
				}
			}
		}
	}()

	go func() {
		scanner := bufio.NewScanner(stderr)
		for scanner.Scan() {
			// fmt.Println("stderr:", scanner.Text())
			logStr := StripANSI(scanner.Text())
			fmt.Println("触发监听器 错误日志：", logStr)
			logInfo := &TLogInfo{
				Log:     logStr,
				Id:      task.Id,
				LogType: "errIdLog",
			}
			v, _ := json.Marshal(logInfo)
			runtime.EventsEmit(a.ctx, "log", string(v))
		}
	}()

	logStr := "后台打包中..."
	// fmt.Println("实例id:", cmd.Process.Pid)
	// logStr += StripANSI(taskInfo.BuildOut.String()) + "\n"
	// fmt.Println(taskInfo.BuildOut.String())

	// logStr += "\n打包成功,文件目录:" + task.Dir + "/dist"
	// fmt.Println("进行压缩文件")
	// if task.Zip {
	// 	zipFileName := task.Dir + "/dist/dist.zip"
	// 	utils.ZipFolder(task.Dir+"/dist", zipFileName)
	// 	logStr += "\n压缩成功,文件" + zipFileName
	// 	fmt.Println("压缩成功,文件" + zipFileName)
	// }
	// fmt.Println("返回")
	return logStr
}

// 拉取 git
func (a *App) GitPull(taskStr string) string {
	fmt.Println("GitPull")
	fmt.Println(taskStr)
	// 将字符串转换为Task结构体
	var task Task
	err := json.Unmarshal([]byte(taskStr), &task)
	if err != nil {
		return "Error unmarshalling task:" + err.Error()
	}
	params := []string{"pull"}
	// ctx, cancel := context.WithCancel(context.Background())
	// TaskCancle[task.Id] = cancel
	// cmd := exec.CommandContext(ctx, task.Cmd, params...)
	cmd := exec.Command("git", params...)
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	cmd.Dir = task.Dir
	TaskCmd[task.Id] = TaskCmdInfo{
		Cmd:    cmd,
		Out:    &bytes.Buffer{},
		ErrOut: &bytes.Buffer{},
	}
	// cmd.Stdout = TaskCmd[task.Id].Out
	// cmd.Stderr = TaskCmd[task.Id].ErrOut

	fmt.Println("task", task)

	// 获取命令的标准输出和标准错误
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("Error getting stdout pipe:", err)
		return "错误获取标准输出"
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		fmt.Println("Error getting stderr pipe:", err)
		return "错误获取标准错误"
	}

	// 启动命令
	if err := cmd.Start(); err != nil {
		fmt.Println("Error starting command:", err)
		return "启动失败"
	}

	// 创建两个goroutine来持续监听标准输出和标准错误
	go func() {
		scanner := bufio.NewScanner(stdout)
		for scanner.Scan() {
			// fmt.Println("stdout:", scanner.Text())
			logStr := StripANSI(scanner.Text())
			fmt.Println("git 运行日志：", logStr)
			logInfo := &TLogInfo{
				Log:     logStr,
				Id:      task.Id,
				LogType: "gitIdLog",
			}
			v, _ := json.Marshal(logInfo)
			runtime.EventsEmit(a.ctx, "log", string(v))
		}
	}()

	go func() {
		scanner := bufio.NewScanner(stderr)
		for scanner.Scan() {
			// fmt.Println("stderr:", scanner.Text())
			logStr := StripANSI(scanner.Text())
			fmt.Println("git 错误日志：", logStr)
			logInfo := &TLogInfo{
				Log:     logStr,
				Id:      task.Id,
				LogType: "gitIdLog",
			}
			v, _ := json.Marshal(logInfo)
			runtime.EventsEmit(a.ctx, "log", string(v))
		}
	}()

	// 等待命令完成
	// if err := cmd.Wait(); err != nil {
	// 	fmt.Println("Command finished with error:", err)
	// } else {
	// 	fmt.Println("Command finished successfully")
	// }
	fmt.Println("实例id:", cmd.Process.Pid)
	fmt.Println("...等待...")
	limit := 1
	lastLog := ""
	for {
		time.Sleep(time.Second * 1)
		limit += 1
		if TaskCmd[task.Id].Out.String() != lastLog {
			lastLog = TaskCmd[task.Id].Out.String()
			fmt.Println("信息改变:", time.Now().Format("2006-01-02 15:04:05"))
		}
		if limit > 2 && lastLog == TaskCmd[task.Id].Out.String() {
			fmt.Println("大于2秒 且 信息没有变动")
			break
		}
	}
	return "ok"
}

// 查找指定端口的进程id
func GetFindPortPid(port string) string {
	pid := ""
	// port := "8110" // 指定要查看的端口
	// 执行 netstat -ano | findstr :8110 命令
	cmd := exec.Command("cmd", "/C", "netstat -ano | findstr :"+port)
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	var out bytes.Buffer
	cmd.Stdout = &out
	if err := cmd.Run(); err != nil {
		fmt.Println("Error executing command:", err)
		return pid
	}

	// 解析命令输出
	re := regexp.MustCompile(`TCP\s+\S+:(\d+)\s+\S+\s+LISTENING\s+(\d+)`)
	matches := re.FindAllStringSubmatch(out.String(), -1)

	for _, match := range matches {
		if match[1] == port {
			pid = match[2]
			fmt.Printf("Port %s is occupied by PID: %s\n", port, pid)
			return pid
		}
	}

	fmt.Printf("Port %s is not occupied pid:%s.\n", port, pid)
	return pid
}

// 杀死进程 pid
func KillPid(pid string) {
	// pid := "10048" // 指定要终止的进程ID

	// 执行 taskkill /f /pid 10048 命令
	cmd := exec.Command("cmd", "/C", "taskkill /f /pid "+pid)
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	if err := cmd.Run(); err != nil {
		fmt.Println("Error executing command:", err)
		return
	}

	fmt.Printf("Successfully terminated process with PID: %s\n", pid)
}

// GbkToUtf8 将GBK编码转换为UTF-8编码
func GbkToUtf8(s []byte) ([]byte, error) {
	reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder())
	d, e := ioutil.ReadAll(reader)
	if e != nil {
		return nil, e
	}
	return d, nil
}

// stripANSI 去除字符串中的ANSI转义序列
func StripANSI(input string) string {
	// 正则表达式匹配ANSI转义序列
	re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
	return ByteToString([]byte(re.ReplaceAllString(input, "")), "GB18030")
}

func convertEncoding(input, fromEncoding, toEncoding string) (string, error) {
	// 使用iconv进行编码转换
	cmd := exec.Command("iconv", "-f", fromEncoding, "-t", toEncoding)
	cmd.Stdin = strings.NewReader(input)
	output, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return string(output), nil
}

// 对字符进行转码
func ByteToString(byte []byte, charset charset) string {
	var str string
	switch charset {
	case GB18030:
		var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
		str = string(decodeBytes)
	case UTF8:
		fallthrough
	default:
		str = string(byte)
	}
	return str
}
